/******************************************************************************
 * Copyright 2022 The Airos Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "v2x_encode_component.h"

#include <sys/time.h>

#include "gflags/gflags.h"

namespace os {
namespace v2x {
namespace protocol {

DEFINE_string(asn_version, "4layer", "v2x message Asn.1 file version");
DEFINE_int32(xml_map_send_rate, 1000, "xml map send rate ms");
DEFINE_string(xml_map_file, "/home/caros/rsu_map.xml", "xml map file name");

bool AIROS_COMPONENT_CLASS_NAME(V2xEncodeComponent)::Init() {
  std::string asn_version(FLAGS_asn_version);
  if (asn_version.compare("4layer") == 0) {
    asn_type_ = EnAsnType::CASE_53_2020;
  } else if (asn_version.compare("new_4layer") == 0) {
    asn_type_ = EnAsnType::YDT_3709_2020;
  } else {
    // pass
  }

  ThreadMap();

  return true;
}

bool AIROS_COMPONENT_CLASS_NAME(V2xEncodeComponent)::Proc(
    const std::shared_ptr<const v2xpb::asn::MessageFrame>& frame) {
  if (!frame) {
    AWARN << "frame is nullptr";
    return false;
  }
  auto encode_pb = std::make_shared<os::v2x::device::RSUData>();
  if (!MessageFrame2RsuPb(frame, encode_pb)) {
    AWARN << "message frame to rsupb failed";
    return false;
  }
  Send("/v2x/device/rsu_in", encode_pb);
  return true;
}

bool AIROS_COMPONENT_CLASS_NAME(V2xEncodeComponent)::MessageFrame2RsuPb(
    const std::shared_ptr<const v2xpb::asn::MessageFrame>& frame,
    std::shared_ptr<os::v2x::device::RSUData> encode_pb) {
  if (!frame || !encode_pb) {
    AWARN << "input is nullptr";
    return false;
  }

  os::v2x::device::RSUDataType data_type;
  switch (frame->payload_case()) {
    case v2xpb::asn::MessageFrame::PayloadCase::kBsmFrame:
      data_type = os::v2x::device::RSU_BSM;
      AINFO << "recv msg pb bsm";
      break;
    case v2xpb::asn::MessageFrame::PayloadCase::kMapFrame:
      data_type = os::v2x::device::RSU_MAP;
      AINFO << "recv msg pb map";
      break;
    case v2xpb::asn::MessageFrame::PayloadCase::kRsmFrame:
      data_type = os::v2x::device::RSU_RSM;
      AINFO << "recv msg pb rsm";
      break;
    case v2xpb::asn::MessageFrame::PayloadCase::kSpatFrame:
      data_type = os::v2x::device::RSU_SPAT;
      AINFO << "recv msg pb spat";
      break;
    case v2xpb::asn::MessageFrame::PayloadCase::kRsiFrame:
      data_type = os::v2x::device::RSU_RSI;
      AINFO << "recv msg pb rsi";
      break;
    default:
      AWARN << "msg type not support ";
      return false;
  }

  std::string str_pb;
  if (!frame->SerializePartialToString(&str_pb)) {
    AWARN << "pb serialize false";
    return false;
  }

  std::string str_asn;
  if (0 > message_frame_pbstr2uper_adapter(str_pb, &str_asn, asn_type_)) {
    AWARN << "pb to asn false";
    return false;
  }

  if (asn_type_ == EnAsnType::YDT_3709_2020) {
    encode_pb->set_version(os::v2x::device::YDT_3709_2020);
  } else {
    encode_pb->set_version(os::v2x::device::CSAE_53_2020);
  }

  struct timeval tv;
  gettimeofday(&tv, NULL);
  encode_pb->set_time_stamp(
      static_cast<uint64_t>(tv.tv_sec * 1000 + tv.tv_usec / 1000));
  encode_pb->set_data((const void*)(str_asn.data()),
                      static_cast<size_t>(str_asn.size()));
  encode_pb->set_type(data_type);

  return true;
}

void AIROS_COMPONENT_CLASS_NAME(V2xEncodeComponent)::ThreadMap() {
  xml_map_send_rate_ = FLAGS_xml_map_send_rate;
  xml_map_file_ = FLAGS_xml_map_file;
  AINFO << "xml map:" << xml_map_send_rate_ << " " << xml_map_file_;

  std::fstream fs(xml_map_file_.data(), std::fstream::in);
  if (!fs.good()) {
    AWARN << "xml file open false:" << xml_map_file_;
    return;
  }
  std::stringstream ss;
  ss << fs.rdbuf();
  fs.close();
  std::string content(ss.str());

  if (content.empty()) {
    AWARN << "xml map data null";
    return;
  }

  std::string str_asn("");
  if (0 >= message_frame_map_xml2uper_adapter(content, &str_asn, asn_type_)) {
    AWARN << " map xml to uper:fasle";
    return;
  }

  std::string str_pb("");
  if (0 >= message_frame_uper2pbstr_adapter(str_asn, &str_pb, asn_type_)) {
    AWARN << "asn to pb false";
    return;
  }
  AINFO << "asn to pb true";

  pb_map_ = std::make_shared<v2xpb::asn::MessageFrame>();
  if (pb_map_) {
    pb_map_->ParsePartialFromString(str_pb);
  } else {
    AWARN << "map pb null";
    return;
  }

  sptr_map_.reset(new std::thread([&] {
    while (true) {
      if (pb_map_ && pb_map_->payload_case() ==
                         v2xpb::asn::MessageFrame::PayloadCase::kMapFrame) {
        pb_map_->mutable_mapframe()->set_message_count(msg_cnt_);
        msg_cnt_ = (msg_cnt_ + 1) % 128;
        AINFO << "map msgcnt:" << pb_map_->mapframe().message_count();
      }
      auto encode_pb = std::make_shared<os::v2x::device::RSUData>();
      if (!MessageFrame2RsuPb(pb_map_, encode_pb)) {
        AWARN << "message frame to rsupb failed";
        return false;
      }
      Send("/v2x/device/rsu_in", encode_pb);

      std::this_thread::sleep_for(
          std::chrono::milliseconds(xml_map_send_rate_));
    }
  }));
}

}  // namespace protocol
}  // namespace v2x
}  // namespace os
